#ifndef HASHACTIVITYHANDLERIMPLS_H_JM_20130322
#define HASHACTIVITYHANDLERIMPLS_H_JM_20130322

#include "../HashActivityHandler.h"
#include "../../BoxBaseTypes.h"

namespace jm4bbx {
	
	template<class HashBoxType>
	class HashJoinHandler : public HashActivityHandler<HashBoxType>  {
	public:

		HashJoinHandler(OutBoxType* b, const BoxParamPack& p) : 
		  HashActivityHandler(b, p) { }

		virtual void handleRecord(TransferValueType value, 
			ExtendedHashEntry& entry) override {

				assert(entry.getCount() == 1);

				auto end = entry.plainEntry.value.end();
				for (auto it = entry.plainEntry.value.begin(); it != end; ++it) {		
					BinaryOperationResultIndexType result = {*it, value.getIndex()};
					addOutputData(result);
				}
		} 

		virtual void handleNoRecord(TransferValueType value) override { }
	};


	template<class HashBoxType>
	class HashIntersectionHandler : public HashActivityHandler<HashBoxType>  {
	public:
		HashIntersectionHandler(OutBoxType* b, const BoxParamPack& p) : 
		  HashActivityHandler(b, p) { }


		virtual void handleRecord(TransferValueType value, 
			ExtendedHashEntry& entry) override {
				std::stringstream stream;

				assert(entry.getCount() == 1);				
				
				auto begin = entry.plainEntry.value.begin();
				// Ako nasobnost sa berie pocet indexov riadkov naleziacich pod 
				// kluc, pretoze nechcem ukladat duplikacie (tak sa nepouzije 
				// vlastnost hash mapy ukladat ako duplikacie) ale prida sa len 
				// dalsi index do aktualne zahesovanej polozky.
				if (entry.plainEntry.value.end() != begin) {
					BinaryOperationResultIndex index = {*begin, value.getIndex()};
					if (version_.isBagVersion()) {
						entry.plainEntry.value.erase(begin);
					}
					else {
						entry.plainEntry.value.clear();
					}
					addOutputData(index);
				}
		} 

		virtual void handleNoRecord(TransferValueType value) override { }
	};

	template<class HashBoxType>
	class HashSecondOperandSubtractionHandler : 
		public HashActivityHandler<HashBoxType>  {
	public:
		HashSecondOperandSubtractionHandler(OutBoxType* b, 
			const BoxParamPack& p) : HashActivityHandler(b, p) { }

		typedef typename HashBoxType::HTH::ValueType ValueType;
		typedef typename HashBoxType::HTH::HashTable HashTable;

		void setHashTable(HashTable* hashTable) { hashTable_ = hashTable; }
		/// Pokial je pocetnost zaznamu v hashmape nenulova tak dekrementuje 
		/// pocetnost zaznamu a pokial je nulova (pripadne teoreticky zaporna) 
		/// tak posle zaznam na vystup ako vysleok rozdielu.
		virtual void handleRecord(TransferValueType value,
			ExtendedHashEntry& entry) override {
				assert(entry.getCount() == 1);


				if (version_.isBagVersion()) {
					if (entry.plainEntry.value.isEmpty()) {
						//LOG_D("BagVersion add result " << value.getKey())
							addResult(value);
					}
					else {
						// Ak sa v hashmape nieco nachadza -> zmazem prave jeden 
						// vyskyt (resp. dekrementuj pocetnost zaznamu).
						//LOG_D("BagVersion remove val " << value.getKey())
							entry.plainEntry.value.erase(
							entry.plainEntry.value.begin());
					}
				}
				else {
					// Nerobim nist nakolko zaznam je v hash mape tj. 
					// pri mnozinovom prevedeni sa verie viacnasobny vyskyt ako
					// jediny.
					//LOG_D("SetVersion remove val " << value.getKey())
				}
		}

		/// Pokial sa zaznam v hashmape vobec nenachadza tak sa posle zaznam
		/// na vystup.
		virtual void handleNoRecord(TransferValueType value) override {
			if (version_.isBagVersion()) {
				//LOG_D("BagVersion add result " << value.getKey())
					addResult(value);
			}
			else {
				// V pripade mnozinoveho prevedenia je potrebne zaistit len 
				// jeden vystup takze si to treba niekde  poznacit.
				// K poznaceniu vyuzijem hashmapu.
				auto key = value.getKey();
				// Nachval bez indexu akoze je prazdny tj. ze uz som odmazaval.
				ValueType newValue(/*value.getIndex(),*/ value.getKey());								
				if (hashTable_->addIf(key, newValue, 
					jmlib::HashRowActionCondition::HAS_NOT_KEY)) {
					//	LOG_D("SetVersion insert to hashmap " << value.getKey())
							// Uspech. Pokial medzitym ako som sa dostal sem 
							// niektora paralelena cast vypoctu nevlozila prvok. 
							// Zablokoval som vystup tohto zaznamu ostatnym vetvam 
							// vypoctu takze vlozim na vystup zaznam ja.
						//	LOG_D("SetVersion add result " << value.getKey())
							addResult(value);

				}
			}
		}

		void addResult(const TransferValueType& value) {
			// Stacia informacie len o prvku z nehashovanej casti a naviac 
			// v urcitych pripadoch su len tieto informacie dostupne. Preto
			// je index prvku z prvej tabulky vzdy nezmyselny.
			BinaryOperationResultIndex index = {-1 /*max size_t*/, 
				value.getIndex()};
			addOutputData(index);
		}
	private:
		HashTable* hashTable_;
	};

} // namespace jm4bbx

#endif
